/***************************************************************************
 *   Copyright (c) 2004 Jürgen Riegel <juergen.riegel@web.de>              *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "pch.h"
#include "Base/Console.h"
#include <Base/Tools.h>
#include <App/Application.h>
#include <App/Document.h>

#include "Control.h"
#include "MainWindow.h"
#include "Gui/Application.h"
#include "Gui/Command.h"

#include "MDIView.h"
#include "View3DInventor.h"
#include "View3DInventorViewer.h"
#include "ViewProviderDocumentObject.h"
#include "App/AutoTransaction.h"
#include "App/ComplexGeoData.h"

#include <Gui/Document.h>
FC_LOG_LEVEL_INIT("Gui", true, true)

using namespace Gui;
namespace bp = boost::placeholders;

namespace Gui {

// Pimpl class
struct DocumentP
{
    int        _iWinCount;
    int        _iDocId;
    bool       _isClosing;
    bool       _isModified;
    bool       _isTransacting;
    bool       _changeViewTouchDocument;
    int                         _editMode;
    ViewProvider*               _editViewProvider;
    App::DocumentObject*        _editingObject;
    ViewProviderDocumentObject* _editViewProviderParent;
    std::string                 _editSubname;
    std::string                 _editSubElement;
    Base::Matrix4D              _editingTransform;
    View3DInventorViewer*       _editingViewer;
    std::set<const App::DocumentObject*> _editObjs;

    Application*    _pcAppWnd;
    // the doc/Document
    App::Document*  _pcDocument;
    /// List of all registered views
    std::list<Gui::BaseView*> baseViews;
    /// List of all registered views
    std::list<Gui::BaseView*> passiveViews;
    std::map<const App::DocumentObject*,ViewProviderDocumentObject*> _ViewProviderMap;
    std::map<SoSeparator *,ViewProviderDocumentObject*> _CoinMap;
    std::map<std::string,ViewProvider*> _ViewProviderMapAnnotation;
    std::list<ViewProviderDocumentObject*> _redoViewProviders;

    typedef boost::signals2::connection Connection;
    Connection connectNewObject;
    Connection connectDelObject;
    Connection connectCngObject;
    Connection connectRenObject;
    Connection connectActObject;
    Connection connectSaveDocument;
    Connection connectRestDocument;
    Connection connectStartLoadDocument;
    Connection connectFinishLoadDocument;
    Connection connectShowHidden;
    Connection connectFinishRestoreObject;
    Connection connectExportObjects;
    Connection connectImportObjects;
    Connection connectFinishImportObjects;
    Connection connectUndoDocument;
    Connection connectRedoDocument;
    Connection connectRecomputed;
    Connection connectSkipRecompute;
    Connection connectTransactionAppend;
    Connection connectTransactionRemove;
    Connection connectTouchedObject;
    Connection connectChangePropertyEditor;
    Connection connectChangeDocument;

    typedef boost::signals2::shared_connection_block ConnectionBlock;
    ConnectionBlock connectActObjectBlocker;
    ConnectionBlock connectChangeDocumentBlocker;
};

} // namespace Gui

/* TRANSLATOR Gui::Document */

/// @namespace Gui @class Document

int Document::_iDocCount = 0;

Document::Document(App::Document* pcDocument,Application * app)
{
    d = new DocumentP;
    d->_iWinCount = 1;
    // new instance
    d->_iDocId = (++_iDocCount);
    d->_isClosing = false;
    d->_isModified = false;
    d->_isTransacting = false;
    d->_pcAppWnd = app;
    d->_pcDocument = pcDocument;
    d->_editViewProvider = nullptr;
    d->_editingObject = nullptr;
    d->_editViewProviderParent = nullptr;
    d->_editingViewer = nullptr;
    d->_editMode = 0;

    // Setup the connections
    d->connectNewObject = pcDocument->signalNewObject.connect
        (boost::bind(&Gui::Document::slotNewObject, this, bp::_1));
    d->connectDelObject = pcDocument->signalDeletedObject.connect
        (boost::bind(&Gui::Document::slotDeletedObject, this, bp::_1));
    d->connectCngObject = pcDocument->signalChangedObject.connect
        (boost::bind(&Gui::Document::slotChangedObject, this, bp::_1, bp::_2));
    d->connectRenObject = pcDocument->signalRelabelObject.connect
        (boost::bind(&Gui::Document::slotRelabelObject, this, bp::_1));
    d->connectActObject = pcDocument->signalActivatedObject.connect
        (boost::bind(&Gui::Document::slotActivatedObject, this, bp::_1));
    d->connectActObjectBlocker = boost::signals2::shared_connection_block
        (d->connectActObject, false);
    /*d->connectSaveDocument = pcDocument->signalSaveDocument.connect
        (boost::bind(&Gui::Document::Save, this, bp::_1));
    d->connectRestDocument = pcDocument->signalRestoreDocument.connect
        (boost::bind(&Gui::Document::Restore, this, bp::_1));*/
    /*d->connectStartLoadDocument = App::GetApplication().signalStartRestoreDocument.connect
        (boost::bind(&Gui::Document::slotStartRestoreDocument, this, bp::_1));
    d->connectFinishLoadDocument = App::GetApplication().signalFinishRestoreDocument.connect
        (boost::bind(&Gui::Document::slotFinishRestoreDocument, this, bp::_1));
    d->connectShowHidden = App::GetApplication().signalShowHidden.connect
        (boost::bind(&Gui::Document::slotShowHidden, this, bp::_1));*/

    d->connectChangePropertyEditor = pcDocument->signalChangePropertyEditor.connect
        (boost::bind(&Gui::Document::slotChangePropertyEditor, this, bp::_1, bp::_2));
    d->connectChangeDocument = d->_pcDocument->signalChanged.connect // use the same slot function
        (boost::bind(&Gui::Document::slotChangePropertyEditor, this, bp::_1, bp::_2));
    d->connectChangeDocumentBlocker = boost::signals2::shared_connection_block
        (d->connectChangeDocument, true);
    d->connectFinishRestoreObject = pcDocument->signalFinishRestoreObject.connect
        (boost::bind(&Gui::Document::slotFinishRestoreObject, this, bp::_1));
    /*d->connectExportObjects = pcDocument->signalExportViewObjects.connect
        (boost::bind(&Gui::Document::exportObjects, this, bp::_1, bp::_2));
    d->connectImportObjects = pcDocument->signalImportViewObjects.connect
        (boost::bind(&Gui::Document::importObjects, this, bp::_1, bp::_2, bp::_3));*/
    d->connectFinishImportObjects = pcDocument->signalFinishImportObjects.connect
        (boost::bind(&Gui::Document::slotFinishImportObjects, this, bp::_1));

    d->connectUndoDocument = pcDocument->signalUndo.connect
        (boost::bind(&Gui::Document::slotUndoDocument, this, bp::_1));
    d->connectRedoDocument = pcDocument->signalRedo.connect
        (boost::bind(&Gui::Document::slotRedoDocument, this, bp::_1));
    d->connectRecomputed = pcDocument->signalRecomputed.connect
        (boost::bind(&Gui::Document::slotRecomputed, this, bp::_1));
    d->connectSkipRecompute = pcDocument->signalSkipRecompute.connect
        (boost::bind(&Gui::Document::slotSkipRecompute, this, bp::_1, bp::_2));
    d->connectTouchedObject = pcDocument->signalTouchedObject.connect
        (boost::bind(&Gui::Document::slotTouchedObject, this, bp::_1));

    d->connectTransactionAppend = pcDocument->signalTransactionAppend.connect
        (boost::bind(&Gui::Document::slotTransactionAppend, this, bp::_1, bp::_2));
    d->connectTransactionRemove = pcDocument->signalTransactionRemove.connect
        (boost::bind(&Gui::Document::slotTransactionRemove, this, bp::_1, bp::_2));
    // pointer to the python class
    // NOTE: As this Python object doesn't get returned to the interpreter we
    // mustn't increment it (Werner Jan-12-2006)

    if (true) {
        d->_pcDocument->setUndoMode(1);
        // set the maximum stack size
        d->_pcDocument->setMaxUndoStackSize(20);
    }

    d->_changeViewTouchDocument =true;
}

Document::~Document()
{
    // disconnect everything to avoid to be double-deleted
    // in case an exception is raised somewhere
    d->connectNewObject.disconnect();
    d->connectDelObject.disconnect();
    d->connectCngObject.disconnect();
    d->connectRenObject.disconnect();
    d->connectActObject.disconnect();
    d->connectSaveDocument.disconnect();
    d->connectRestDocument.disconnect();
    d->connectStartLoadDocument.disconnect();
    d->connectFinishLoadDocument.disconnect();
    d->connectShowHidden.disconnect();
    d->connectFinishRestoreObject.disconnect();
    d->connectExportObjects.disconnect();
    d->connectImportObjects.disconnect();
    d->connectFinishImportObjects.disconnect();
    d->connectUndoDocument.disconnect();
    d->connectRedoDocument.disconnect();
    d->connectRecomputed.disconnect();
    d->connectSkipRecompute.disconnect();
    d->connectTransactionAppend.disconnect();
    d->connectTransactionRemove.disconnect();
    d->connectTouchedObject.disconnect();
    d->connectChangePropertyEditor.disconnect();
    d->connectChangeDocument.disconnect();

    // e.g. if document gets closed from within a Python command
    d->_isClosing = true;
    // calls Document::detachView() and alter the view list
    std::list<Gui::BaseView*> temp = d->baseViews;
    for(std::list<Gui::BaseView*>::iterator it=temp.begin();it!=temp.end();++it)
        (*it)->deleteSelf();

    std::map<const App::DocumentObject*,ViewProviderDocumentObject*>::iterator jt;
    for (jt = d->_ViewProviderMap.begin();jt != d->_ViewProviderMap.end(); ++jt)
        delete jt->second;
    std::map<std::string,ViewProvider*>::iterator it2;
    for (it2 = d->_ViewProviderMapAnnotation.begin();it2 != d->_ViewProviderMapAnnotation.end(); ++it2)
        delete it2->second;

    // remove the reference from the object

    delete d;
}

//*****************************************************************************************************
// 3D viewer handling
//*****************************************************************************************************

bool Document::setEdit(Gui::ViewProvider* p, int ModNum, const char *subname)
{
        ViewProviderDocumentObject* vp = dynamic_cast<ViewProviderDocumentObject*>(p);
    if (!vp) {
        FC_ERR("cannot edit non ViewProviderDocumentObject");
        return false;
    }

    // Fix regression: https://forum.freecadweb.org/viewtopic.php?f=19&t=43629&p=371972#p371972
    // When an object is already in edit mode a subsequent call for editing is only possible
    // when resetting the currently edited object.
    if (d->_editViewProvider) {
        _resetEdit();
    }

    auto obj = vp->getObject();
    if(!obj->getNameInDocument()) {
        FC_ERR("cannot edit detached object");
        return false;
    }

    std::string _subname;
    if(!subname || !subname[0]) {
        // No subname reference is given, we try to extract one from the current
        // selection in order to obtain the correct transformation matrix below
        auto sels = Gui::Selection().getCompleteSelection(ResolveMode::NoResolve);
        App::DocumentObject *parentObj = nullptr;
        for(auto &sel : sels) {
            if(!sel.pObject || !sel.pObject->getNameInDocument())
                continue;
            if(!parentObj)
                parentObj = sel.pObject;
            else if(parentObj!=sel.pObject) {
                FC_LOG("Cannot deduce subname for editing, more than one parent?");
                parentObj = nullptr;
                break;
            }
            auto sobj = parentObj->getSubObject(sel.SubName);
            if(!sobj || (sobj!=obj && sobj->getLinkedObject(true)!= obj)) {
                FC_LOG("Cannot deduce subname for editing, subname mismatch");
                parentObj = nullptr;
                break;
            }
            _subname = sel.SubName;
        }
        if(parentObj) {
            FC_LOG("deduced editing reference " << parentObj->getFullName() << '.' << _subname);
            subname = _subname.c_str();
            obj = parentObj;
            vp = dynamic_cast<ViewProviderDocumentObject*>(
                    Application::Instance->getViewProvider(obj));
            if(!vp || !vp->getDocument()) {
                FC_ERR("invliad view provider for parent object");
                return false;
            }
            if(vp->getDocument()!=this)
                return vp->getDocument()->setEdit(vp,ModNum,subname);
        }
    }

    if (d->_ViewProviderMap.find(obj) == d->_ViewProviderMap.end()) {
        // We can actually support editing external object, by calling
        // View3DInventViewer::setupEditingRoot() before exiting from
        // ViewProvider::setEditViewer(), which transfer all child node of the view
        // provider into an editing node inside the viewer of this document. And
        // that's may actually be the case, as the subname referenced sub object
        // is allowed to be in other documents.
        //
        // We just disabling editing external parent object here, for bug
        // tracking purpose. Because, bringing an unrelated external object to
        // the current view for editing will confuse user, and is certainly a
        // bug. By right, the top parent object should always belong to the
        // editing document, and the actually editing sub object can be
        // external.
        //
        // So, you can either call setEdit() with subname set to 0, which cause
        // the code above to auto detect selection context, and dispatch the
        // editing call to the correct document. Or, supply subname yourself,
        // and make sure you get the document right.
        //
        FC_ERR("cannot edit object '" << obj->getNameInDocument() << "': not found in document "
                << "'" << getDocument()->getName() << "'");
        return false;
    }

    d->_editingTransform = Base::Matrix4D();
    // Geo feature group now handles subname like link group. So no need of the
    // following code.
    //
    // if(!subname || !subname[0]) {
    //     auto group = App::GeoFeatureGroupExtension::getGroupOfObject(obj);
    //     if(group) {
    //         auto ext = group->getExtensionByType<App::GeoFeatureGroupExtension>();
    //         d->_editingTransform = ext->globalGroupPlacement().toMatrix();
    //     }
    // }
    auto sobj = obj->getSubObject(subname,nullptr,&d->_editingTransform);
    if(!sobj || !sobj->getNameInDocument()) {
        FC_ERR("Invalid sub object '" << obj->getFullName()
                << '.' << (subname?subname:"") << "'");
        return false;
    }
    auto svp = vp;
    if(sobj!=obj) {
        svp = dynamic_cast<ViewProviderDocumentObject*>(
                Application::Instance->getViewProvider(sobj));
        if(!svp) {
            FC_ERR("Cannot edit '" << sobj->getFullName() << "' without view provider");
            return false;
        }
    }

    View3DInventor *view3d = dynamic_cast<View3DInventor *>(getActiveView());
    // if the currently active view is not the 3d view search for it and activate it
    if (view3d) {
        // getMainWindow()->setActiveWindow(view3d);
    }
    else{view3d = dynamic_cast<View3DInventor *>(setActiveView(vp));}
        
    Application::Instance->setEditDocument(this);

    d->_editViewProviderParent = vp;
    d->_editSubElement.clear();
    d->_editSubname.clear();

    if (subname) {
        const char *element = Data::ComplexGeoData::findElementName(subname);
        if (element) {
            d->_editSubname = std::string(subname,element-subname);
            d->_editSubElement = element;
        }
        else {
            d->_editSubname = subname;
        }
    }

    auto sobjs = obj->getSubObjectList(subname);
    d->_editObjs.clear();
    d->_editObjs.insert(sobjs.begin(),sobjs.end());
    d->_editingObject = sobj;

    d->_editMode = ModNum;
    d->_editViewProvider = svp->startEditing(ModNum);
    if(!d->_editViewProvider) {
        d->_editViewProviderParent = nullptr;
        d->_editObjs.clear();
        d->_editingObject = nullptr;
        FC_LOG("object '" << sobj->getFullName() << "' refuse to edit");
        return false;
    }

    if(view3d) {
        view3d->getViewer()->setEditingViewProvider(d->_editViewProvider,ModNum);
        d->_editingViewer = view3d->getViewer();
    }
    Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog();
    if (dlg)
        dlg->setDocumentName(this->getDocument()->getName());
    if (d->_editViewProvider->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) {
        auto vpd = static_cast<ViewProviderDocumentObject*>(d->_editViewProvider);
        vpd->getDocument()->signalInEdit(*vpd);
    }

    App::AutoTransaction::setEnable(false);
    return true;
}

const Base::Matrix4D &Document::getEditingTransform() const {
    return d->_editingTransform;
}

void Document::setEditingTransform(const Base::Matrix4D &mat) {
    d->_editObjs.clear();
    d->_editingTransform = mat;
    View3DInventor *activeView = dynamic_cast<View3DInventor *>(getActiveView());
    if (activeView)
        activeView->getViewer()->setEditingTransform(mat);
}

void Document::resetEdit() {
    Application::Instance->setEditDocument(nullptr);
}

void Document::_resetEdit()
{
    std::list<Gui::BaseView*>::iterator it;
    if (d->_editViewProvider) {
        for (it = d->baseViews.begin();it != d->baseViews.end();++it) {
            View3DInventor *activeView = dynamic_cast<View3DInventor *>(*it);
            if (activeView)
                activeView->getViewer()->resetEditingViewProvider();
        }

        d->_editViewProvider->finishEditing();

        // Have to check d->_editViewProvider below, because there is a chance
        // the editing object gets deleted inside the above call to
        // 'finishEditing()', which will trigger our slotDeletedObject(), which
        // nullifies _editViewProvider.
        if (d->_editViewProvider && d->_editViewProvider->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) {
            auto vpd = static_cast<ViewProviderDocumentObject*>(d->_editViewProvider);
            vpd->getDocument()->signalResetEdit(*vpd);
        }
        d->_editViewProvider = nullptr;

        // The logic below is not necessary anymore, because this method is
        // changed into a private one,  _resetEdit(). And the exposed
        // resetEdit() above calls into Application->setEditDocument(0) which
        // will prevent recursive calling.
#if 0
        // Nullify the member variable before calling finishEditing().
        // This is to avoid a possible stack overflow when a view provider wrongly
        // invokes the document's resetEdit() method.
        ViewProvider* editViewProvider = d->_editViewProvider;
        d->_editViewProvider = nullptr;

        editViewProvider->finishEditing();
        if (editViewProvider->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId()))
            signalResetEdit(*(static_cast<ViewProviderDocumentObject*>(editViewProvider)));
#endif
        App::GetApplication().closeActiveTransaction();
    }
    d->_editViewProviderParent = nullptr;
    d->_editingViewer = nullptr;
    d->_editObjs.clear();
    d->_editingObject = nullptr;
    if(Application::Instance->editDocument() == this)
        Application::Instance->setEditDocument(nullptr);
}

ViewProvider *Document::getInEdit(ViewProviderDocumentObject **parentVp,
        std::string *subname, int *mode, std::string *subelement) const
{
    if(parentVp) *parentVp = d->_editViewProviderParent;
    if(subname) *subname = d->_editSubname;
    if(subelement) *subelement = d->_editSubElement;
    if(mode) *mode = d->_editMode;

    if (d->_editViewProvider) {
        // there is only one 3d view which is in edit mode
        View3DInventor *activeView = dynamic_cast<View3DInventor *>(getActiveView());
        if (activeView && activeView->getViewer()->isEditingViewProvider())
            return d->_editViewProvider;
    }

    return nullptr;
}

void Document::setInEdit(ViewProviderDocumentObject *parentVp, const char *subname) {
    if (d->_editViewProvider) {
        d->_editViewProviderParent = parentVp;
        d->_editSubname = subname?subname:"";
    }
}

void Document::setAnnotationViewProvider(const char* name, ViewProvider *pcProvider)
{
    std::list<Gui::BaseView*>::iterator vIt;

    // already in ?
    std::map<std::string,ViewProvider*>::iterator it = d->_ViewProviderMapAnnotation.find(name);
    if (it != d->_ViewProviderMapAnnotation.end())
        removeAnnotationViewProvider(name);

    // add
    d->_ViewProviderMapAnnotation[name] = pcProvider;

    // cycling to all views of the document
    for (vIt = d->baseViews.begin();vIt != d->baseViews.end();++vIt) {
        View3DInventor *activeView = dynamic_cast<View3DInventor *>(*vIt);
        if (activeView)
            activeView->getViewer()->addViewProvider(pcProvider);
    }
}

ViewProvider * Document::getAnnotationViewProvider(const char* name) const
{
    std::map<std::string,ViewProvider*>::const_iterator it = d->_ViewProviderMapAnnotation.find(name);
    return ( (it != d->_ViewProviderMapAnnotation.end()) ? it->second : 0 );
}

void Document::removeAnnotationViewProvider(const char* name)
{
    std::map<std::string,ViewProvider*>::iterator it = d->_ViewProviderMapAnnotation.find(name);
    std::list<Gui::BaseView*>::iterator vIt;

    // cycling to all views of the document
    for (vIt = d->baseViews.begin();vIt != d->baseViews.end();++vIt) {
        View3DInventor *activeView = dynamic_cast<View3DInventor *>(*vIt);
        if (activeView)
            activeView->getViewer()->removeViewProvider(it->second);
    }

    delete it->second;
    d->_ViewProviderMapAnnotation.erase(it);
}


ViewProvider* Document::getViewProvider(const App::DocumentObject* Feat) const
{
    std::map<const App::DocumentObject*,ViewProviderDocumentObject*>::const_iterator
    it = d->_ViewProviderMap.find( Feat );
    return ( (it != d->_ViewProviderMap.end()) ? it->second : 0 );
}

std::vector<ViewProvider*> Document::getViewProvidersOfType(const Base::Type& typeId) const
{
    std::vector<ViewProvider*> Objects;
    for (std::map<const App::DocumentObject*,ViewProviderDocumentObject*>::const_iterator it =
         d->_ViewProviderMap.begin(); it != d->_ViewProviderMap.end(); ++it ) {
        if (it->second->getTypeId().isDerivedFrom(typeId))
            Objects.push_back(it->second);
    }
    return Objects;
}

ViewProvider *Document::getViewProviderByName(const char* name) const
{
    // first check on feature name
    App::DocumentObject *pcFeat = getDocument()->getObject(name);

    if (pcFeat)
    {
        std::map<const App::DocumentObject*,ViewProviderDocumentObject*>::const_iterator
        it = d->_ViewProviderMap.find( pcFeat );

        if (it != d->_ViewProviderMap.end())
            return it->second;
    } else {
        // then try annotation name
        std::map<std::string,ViewProvider*>::const_iterator it2 = d->_ViewProviderMapAnnotation.find( name );

        if (it2 != d->_ViewProviderMapAnnotation.end())
            return it2->second;
    }

    return nullptr;
}

bool Document::isShow(const char* name)
{
    ViewProvider* pcProv = getViewProviderByName(name);
    return pcProv ? pcProv->isShow() : false;
}

/// put the feature in show
void Document::setShow(const char* name){}

/// set the feature in Noshow
void Document::setHide(const char* name){}

/// set the feature in Noshow
void Document::setPos(const char* name, const Base::Matrix4D& rclMtrx)
{
    ViewProvider* pcProv = getViewProviderByName(name);
    if (pcProv)
        pcProv->setTransformation(rclMtrx);

}

//*****************************************************************************************************
// Document
//*****************************************************************************************************
void Document::slotNewObject(const App::DocumentObject& Obj) {
        ViewProviderDocumentObject* pcProvider = static_cast<ViewProviderDocumentObject*>(getViewProvider(&Obj));
    if (!pcProvider) {
        //Base::Console().Log("Document::slotNewObject() called\n");
        std::string cName = Obj.getViewProviderNameStored();
        for(;;) {
            if (cName.empty()) {
                // handle document object with no view provider specified
                FC_LOG(Obj.getFullName() << " has no view provider specified");
                return;
            }
            Base::Type type = Base::Type::getTypeIfDerivedFrom(cName.c_str(), ViewProviderDocumentObject::getClassTypeId(), true);
            pcProvider = static_cast<ViewProviderDocumentObject*>(type.createInstance());
            // createInstance could return a null pointer
            if (!pcProvider) {
                // type not derived from ViewProviderDocumentObject!!!
                FC_ERR("Invalid view provider type '" << cName << "' for " << Obj.getFullName());
                return;
            }
            else if (cName!=Obj.getViewProviderName() && !pcProvider->allowOverride(Obj)) {
                FC_WARN("View provider type '" << cName << "' does not support " << Obj.getFullName());
                pcProvider = nullptr;
                cName = Obj.getViewProviderName();
            }
             else {
                break;
            }
        }

        setModified(true);
        d->_ViewProviderMap[&Obj] = pcProvider;
        d->_CoinMap[pcProvider->getRoot()] = pcProvider;
        pcProvider->setStatus(Gui::ViewStatus::TouchDocument, d->_changeViewTouchDocument);

        try {
            // if successfully created set the right name and calculate the view
            //FIXME: Consider to change argument of attach() to const pointer
            pcProvider->attach(const_cast<App::DocumentObject*>(&Obj));
            pcProvider->updateView();
            pcProvider->setActiveMode();
        }
        catch(const Base::MemoryException& e){
            FC_ERR("Memory exception in " << Obj.getFullName() << " thrown: " << e.what());
        }
        catch(Base::Exception &e){
            e.ReportException();
        }
#ifndef FC_DEBUG
        catch(...){
            FC_ERR("Unknown exception in Feature " << Obj.getFullName() << " thrown");
        }
#endif
    }else{
        try {
            pcProvider->reattach(const_cast<App::DocumentObject*>(&Obj));
        } catch(Base::Exception &e){
            e.ReportException();
        }
    }

    if (pcProvider) {
        std::list<Gui::BaseView*>::iterator vIt;
        // cycling to all views of the document
        for (vIt = d->baseViews.begin();vIt != d->baseViews.end();++vIt) {
            View3DInventor *activeView = dynamic_cast<View3DInventor *>(*vIt);
            if (activeView)
                activeView->getViewer()->addViewProvider(pcProvider);
        }

        // adding to the tree
        signalNewObject(*pcProvider);
        pcProvider->pcDocument = this;

        // it is possible that a new viewprovider already claims children
        handleChildren3D(pcProvider);
        if (d->_isTransacting) {
            d->_redoViewProviders.push_back(pcProvider);
        }
    }
}

void Document::slotDeletedObject(const App::DocumentObject& Obj)
{
    std::list<Gui::BaseView*>::iterator vIt;
    setModified(true);
    //Base::Console().Log("Document::slotDeleteObject() called\n");

    // cycling to all views of the document
    ViewProvider* viewProvider = getViewProvider(&Obj);
    if(!viewProvider)
        return;

    if (d->_editViewProvider==viewProvider || d->_editViewProviderParent==viewProvider)
        _resetEdit();
    else if(Application::Instance->editDocument()) {
        auto editDoc = Application::Instance->editDocument();
        if(editDoc->d->_editViewProvider==viewProvider ||
           editDoc->d->_editViewProviderParent==viewProvider)
            Application::Instance->setEditDocument(nullptr);
    }

    handleChildren3D(viewProvider,true);

#if 0 // With this we can show child objects again if this method was called by undo
    viewProvider->onDelete(std::vector<std::string>());
#endif
    if (viewProvider && viewProvider->getTypeId().isDerivedFrom
        (ViewProviderDocumentObject::getClassTypeId())) {
        // go through the views
        for (vIt = d->baseViews.begin();vIt != d->baseViews.end();++vIt) {
            View3DInventor *activeView = dynamic_cast<View3DInventor *>(*vIt);
            if (activeView)
                activeView->getViewer()->removeViewProvider(viewProvider);
        }

        // removing from tree
        signalDeletedObject(*(static_cast<ViewProviderDocumentObject*>(viewProvider)));
    }

    viewProvider->beforeDelete();
}

void Document::beforeDelete() {
    auto editDoc = Application::Instance->editDocument();
    if(editDoc) {
        auto vp = dynamic_cast<ViewProviderDocumentObject*>(editDoc->d->_editViewProvider);
        auto vpp = dynamic_cast<ViewProviderDocumentObject*>(editDoc->d->_editViewProviderParent);
        if(editDoc == this ||
           (vp && vp->getDocument()==this) ||
           (vpp && vpp->getDocument()==this))
        {
            Application::Instance->setEditDocument(nullptr);
        }
    }
    for(auto &v : d->_ViewProviderMap)
        v.second->beforeDelete();
}

void Document::slotChangedObject(const App::DocumentObject& Obj, const App::Property& Prop){
    //Base::Console().Log("Document::slotChangedObject() called\n");
    ViewProvider* viewProvider = getViewProvider(&Obj);
    if (viewProvider) {
        try {
            viewProvider->update(&Prop);
            if(d->_editingViewer
                    && d->_editingObject
                    && d->_editViewProviderParent
                    && (Prop.isDerivedFrom(App::PropertyPlacement::getClassTypeId())
                        // Issue ID 0004230 : getName() can return null in which case strstr() crashes
                        || (Prop.getName() && strstr(Prop.getName(),"Scale")))
                    && d->_editObjs.count(&Obj))
            {
                Base::Matrix4D mat;
                auto sobj = d->_editViewProviderParent->getObject()->getSubObject(
                                                        d->_editSubname.c_str(),nullptr,&mat);
                if(sobj == d->_editingObject && d->_editingTransform!=mat) {
                    d->_editingTransform = mat;
                    d->_editingViewer->setEditingTransform(d->_editingTransform);
                }
            }
        }
        catch(const Base::MemoryException& e) {
            FC_ERR("Memory exception in " << Obj.getFullName() << " thrown: " << e.what());
        }
        catch(Base::Exception& e){
            e.ReportException();
        }
        catch(const std::exception& e){
            FC_ERR("C++ exception in " << Obj.getFullName() << " thrown " << e.what());
        }
        catch (...) {
            FC_ERR("Cannot update representation for " << Obj.getFullName());
        }

        handleChildren3D(viewProvider);

        if (viewProvider->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId()))
            signalChangedObject(static_cast<ViewProviderDocumentObject&>(*viewProvider), Prop);
    }

    // a property of an object has changed
    if(!Prop.testStatus(App::Property::NoModify) && !isModified()) {
        FC_LOG(Prop.getFullName() << " modified");
        setModified(true);
    }

    getMainWindow()->updateActions(true);
}

void Document::slotRelabelObject(const App::DocumentObject& Obj){}

void Document::slotTransactionAppend(const App::DocumentObject& obj, App::Transaction* transaction){}

void Document::slotTransactionRemove(const App::DocumentObject& obj, App::Transaction* transaction){}

void Document::slotActivatedObject(const App::DocumentObject& Obj)
{
    ViewProvider* viewProvider = getViewProvider(&Obj);
    if (viewProvider && viewProvider->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) {
        signalActivatedObject(*(static_cast<ViewProviderDocumentObject*>(viewProvider)));
    }
}

void Document::slotUndoDocument(const App::Document& doc)
{
    if (d->_pcDocument != &doc)
        return;

    signalUndoDocument(*this);
    // getMainWindow()->updateActions();
}

void Document::slotRedoDocument(const App::Document& doc)
{
    if (d->_pcDocument != &doc)
        return;

    signalRedoDocument(*this);
    // getMainWindow()->updateActions();
}

void Document::slotRecomputed(const App::Document& doc){}

// This function is called when some asks to recompute a document that is marked
// as 'SkipRecompute'. We'll check if we are the current document, and if either
// not given an explicit recomputing object list, or the given single object is
// the eidting object or the active object. If the conditions are met, we'll
// force recompute only that object and all its dependent objects.
void Document::slotSkipRecompute(const App::Document& doc, const std::vector<App::DocumentObject*> &objs){}

void Document::slotTouchedObject(const App::DocumentObject &Obj)
{
    // getMainWindow()->updateActions(true);
    if(!isModified()) {
        FC_LOG(Obj.getFullName() << " touched");
        setModified(true);
    }
}

void Document::addViewProvider(Gui::ViewProviderDocumentObject* vp)
{
    // Hint: The undo/redo first adds the view provider to the Gui
    // document before adding the objects to the App document.

    // the view provider is added by TransactionViewProvider and an
    // object can be there only once
    assert(d->_ViewProviderMap.find(vp->getObject()) == d->_ViewProviderMap.end());
    vp->setStatus(Detach, false);
    d->_ViewProviderMap[vp->getObject()] = vp;
    d->_CoinMap[vp->getRoot()] = vp;
}

void Document::setModified(bool b)
{
    if(d->_isModified == b)
        return;
    d->_isModified = b;

    std::list<MDIView*> mdis = getMDIViews();
    for (std::list<MDIView*>::iterator it = mdis.begin(); it != mdis.end(); ++it) {
        (*it)->setWindowModified(b);
    }
}

bool Document::isModified() const
{
    return d->_isModified;
}


ViewProviderDocumentObject* Document::getViewProviderByPathFromTail(SoPath * path) const{return nullptr;}

ViewProviderDocumentObject* Document::getViewProviderByPathFromHead(SoPath * path) const{return nullptr;}

ViewProviderDocumentObject *Document::getViewProvider(SoNode *node) const{return nullptr;}

std::vector<std::pair<ViewProviderDocumentObject*,int> > Document::getViewProvidersByPath(SoPath * path) const{return std::vector<std::pair<ViewProviderDocumentObject*,int> >();}

App::Document* Document::getDocument() const
{
    return d->_pcDocument;
}

static bool checkCanonicalPath(const std::map<App::Document*, bool> &docs){}

bool Document::askIfSavingFailed(const QString& error){return 0;}

/// Save the document
bool Document::save(){return 0;}

/// Save the document under a new file name
bool Document::saveAs(){return 0;}

void Document::saveAll(){}

/// Save a copy of the document under a new file name
bool Document::saveCopy(){return 0;}

unsigned int Document::getMemSize () const{return 0;}

/**
 * Adds a separate XML file to the projects file that contains information about the view providers.
 */
void Document::Save (Base::Writer &writer) const{}

/**
 * Loads a separate XML file from the projects file with information about the view providers.
 */
void Document::Restore(){}

/**
 * Restores the properties of the view providers.
 */
void Document::RestoreDocFile(){}

void Document::slotStartRestoreDocument(const App::Document& doc)
{
    if (d->_pcDocument != &doc)
        return;
    // disable this signal while loading a document
    d->connectActObjectBlocker.block();
}

void Document::slotFinishRestoreObject(const App::DocumentObject &obj) {
    auto vpd = dynamic_cast<ViewProviderDocumentObject*>(getViewProvider(&obj));
    if(vpd) {
        vpd->setStatus(Gui::isRestoring,false);
        vpd->finishRestoring();
        if(!vpd->canAddToSceneGraph())
            toggleInSceneGraph(vpd);
    }
}

void Document::slotFinishRestoreDocument(const App::Document& doc)
{
    if (d->_pcDocument != &doc)
        return;
    d->connectActObjectBlocker.unblock();
    App::DocumentObject* act = doc.getActiveObject();
    if (act) {
        ViewProvider* viewProvider = getViewProvider(act);
        if (viewProvider && viewProvider->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) {
            signalActivatedObject(*(static_cast<ViewProviderDocumentObject*>(viewProvider)));
        }
    }

    // reset modified flag
    setModified(doc.testStatus(App::Document::LinkStampChanged));
}

void Document::slotShowHidden(const App::Document& doc)
{
    if (d->_pcDocument != &doc)
        return;

    Application::Instance->signalShowHidden(*this);
}

/**
 * Saves the properties of the view providers.
 */
void Document::SaveDocFile (Base::Writer &writer) const{}

void Document::exportObjects(const std::vector<App::DocumentObject*>& obj){}

void Document::importObjects(const std::vector<App::DocumentObject*>& obj,
                             const std::map<std::string, std::string>& nameMapping){}

void Document::slotFinishImportObjects(const std::vector<App::DocumentObject*> &objs) {
    (void)objs;
    // finishRestoring() is now triggered by signalFinishRestoreObject
    //
    // for(auto obj : objs) {
    //     auto vp = getViewProvider(obj);
    //     if(!vp) continue;
    //     vp->setStatus(Gui::isRestoring,false);
    //     auto vpd = dynamic_cast<ViewProviderDocumentObject*>(vp);
    //     if(vpd) vpd->finishRestoring();
    // }
}


void Document::addRootObjectsToGroup(const std::vector<App::DocumentObject*>& obj, App::DocumentObjectGroup* grp){}
void addWindow(QWidget* qwd,MDIView* view)
{
    // make workspace parent of view
    // bool isempty = qwd->subWindowList().isEmpty();
    bool isempty = true;
    QMdiSubWindow* child = qobject_cast<QMdiSubWindow*>(view->parentWidget());
    /*if(!child) {
        child = new QMdiSubWindow(qwd->viewport());
        child->setAttribute(Qt::WA_DeleteOnClose);
        child->setWidget(view);
        child->setWindowIcon(view->windowIcon());
        QMenu* menu = child->systemMenu();

        // See StdCmdCloseActiveWindow (#0002631)
        QList<QAction*> acts = menu->actions();
        for (QList<QAction*>::iterator it = acts.begin(); it != acts.end(); ++it) {
            if ((*it)->shortcut() == QKeySequence(QKeySequence::Close)) {
                (*it)->setShortcuts(QList<QKeySequence>());
                break;
            }
        }
    }*/



    // listen to the incoming events of the view
    view->installEventFilter(qwd);

    // show the very first window in maximized mode
    if (isempty)
        view->showMaximized();
    else
        view->show();
}
MDIView *Document::createView(const Base::Type& typeId) {
        if (!typeId.isDerivedFrom(MDIView::getClassTypeId()))
        return nullptr;

    std::list<MDIView*> theViews = this->getMDIViewsOfType(typeId);
    if (typeId == View3DInventor::getClassTypeId()) {

        QtGLWidget* shareWidget = nullptr;
        // VBO rendering doesn't work correctly when we don't share the OpenGL widgets
        if (!theViews.empty()) {
            View3DInventor* firstView = static_cast<View3DInventor*>(theViews.front());
            shareWidget = qobject_cast<QtGLWidget*>(firstView->getViewer()->getGLWidget());

            const char *ppReturn = nullptr;
            firstView->onMsg("GetCamera",&ppReturn);
            saveCameraSettings(ppReturn);
        }

        // View3DInventor* view3D = new View3DInventor(this, Gui::Application::Instance->main_widget, shareWidget);
        View3DInventor* view3D = new View3DInventor(this, getMainWindow(), shareWidget);
        if (!theViews.empty()) {
            View3DInventor* firstView = static_cast<View3DInventor*>(theViews.front());
            std::string overrideMode = firstView->getViewer()->getOverrideMode();
            view3D->getViewer()->setOverrideMode(overrideMode);
        }

        // attach the viewproviders. we need to make sure that we only attach the toplevel ones
        // and not viewproviders which are claimed by other providers. To ensure this we first
        // add all providers and then remove the ones already claimed
        std::map<const App::DocumentObject*,ViewProviderDocumentObject*>::const_iterator It1;
        std::vector<App::DocumentObject*> child_vps;
        for (It1=d->_ViewProviderMap.begin();It1!=d->_ViewProviderMap.end();++It1) {
            view3D->getViewer()->addViewProvider(It1->second);
            std::vector<App::DocumentObject*> children = It1->second->claimChildren3D();
            child_vps.insert(child_vps.end(), children.begin(), children.end());
        }
        std::map<std::string,ViewProvider*>::const_iterator It2;
        for (It2=d->_ViewProviderMapAnnotation.begin();It2!=d->_ViewProviderMapAnnotation.end();++It2) {
            view3D->getViewer()->addViewProvider(It2->second);
            std::vector<App::DocumentObject*> children = It2->second->claimChildren3D();
            child_vps.insert(child_vps.end(), children.begin(), children.end());
        }

        for (App::DocumentObject* obj : child_vps)
            view3D->getViewer()->removeViewProvider(getViewProvider(obj));

        const char* name = getDocument()->Label.getValue();
        QString title = QString::fromLatin1("%1 : %2[*]")
            .arg(QString::fromUtf8(name)).arg(d->_iWinCount++);

        view3D->setWindowTitle(title);
        view3D->setWindowModified(this->isModified());
        view3D->setWindowIcon(QApplication::windowIcon());
        view3D->resize(400, 300);
        view3D->getViewer()->redraw();

        if (!cameraSettings.empty()) {
            const char *ppReturn = nullptr;
            view3D->onMsg(cameraSettings.c_str(),&ppReturn);
        }

        getMainWindow()->addWindow(view3D);
        // addWindow( Gui::Application::Instance->main_widget,view3D);
        return view3D;
    }
    return nullptr;
}

Gui::MDIView* Document::cloneView(Gui::MDIView* oldview)
{
    if (!oldview)
        return nullptr;

    if (oldview->getTypeId() == View3DInventor::getClassTypeId()) {
        // View3DInventor* view3D = new View3DInventor(this, getMainWindow());
        View3DInventor* view3D = new View3DInventor(this, nullptr);

        View3DInventor* firstView = static_cast<View3DInventor*>(oldview);
        std::string overrideMode = firstView->getViewer()->getOverrideMode();
        view3D->getViewer()->setOverrideMode(overrideMode);

        view3D->getViewer()->setAxisCross(firstView->getViewer()->hasAxisCross());

        // attach the viewproviders. we need to make sure that we only attach the toplevel ones
        // and not viewproviders which are claimed by other providers. To ensure this we first
        // add all providers and then remove the ones already claimed
        std::map<const App::DocumentObject*,ViewProviderDocumentObject*>::const_iterator It1;
        std::vector<App::DocumentObject*> child_vps;
        for (It1=d->_ViewProviderMap.begin();It1!=d->_ViewProviderMap.end();++It1) {
            view3D->getViewer()->addViewProvider(It1->second);
            std::vector<App::DocumentObject*> children = It1->second->claimChildren3D();
            child_vps.insert(child_vps.end(), children.begin(), children.end());
        }
        std::map<std::string,ViewProvider*>::const_iterator It2;
        for (It2=d->_ViewProviderMapAnnotation.begin();It2!=d->_ViewProviderMapAnnotation.end();++It2) {
            view3D->getViewer()->addViewProvider(It2->second);
            std::vector<App::DocumentObject*> children = It2->second->claimChildren3D();
            child_vps.insert(child_vps.end(), children.begin(), children.end());
        }

        for (App::DocumentObject* obj : child_vps)
            view3D->getViewer()->removeViewProvider(getViewProvider(obj));

        view3D->setWindowTitle(oldview->windowTitle());
        view3D->setWindowModified(oldview->isWindowModified());
        view3D->setWindowIcon(oldview->windowIcon());
        view3D->resize(oldview->size());

        // FIXME: Add parameter to define behaviour by the calling instance
        // View provider editing
        if (d->_editViewProvider) {
            firstView->getViewer()->resetEditingViewProvider();
            view3D->getViewer()->setEditingViewProvider(d->_editViewProvider, d->_editMode);
        }

        return view3D;
    }

    return nullptr;
}

const char *Document::getCameraSettings() const {
    return cameraSettings.size()>10?cameraSettings.c_str()+10:cameraSettings.c_str();
}

bool Document::saveCameraSettings(const char *settings) const {
    if(!settings)
        return false;

    // skip starting comment lines
    bool skipping = false;
    char c = *settings;
    for(;c;c=*(++settings)) {
        if(skipping) {
            if(c == '\n')
                skipping = false;
        } else if(c == '#')
            skipping = true;
        else if(!std::isspace(c))
            break;
    }

    if(!c)
        return false;

    cameraSettings = std::string("SetCamera ") + settings;
    return true;
}

void Document::attachView(Gui::BaseView* pcView, bool bPassiv)
{
    if (!bPassiv)
        d->baseViews.push_back(pcView);
    else
        d->passiveViews.push_back(pcView);
}

void Document::detachView(Gui::BaseView* pcView, bool bPassiv){}

void Document::onUpdate()
{
#ifdef FC_LOGUPDATECHAIN
    Base::Console().Log("Acti: Gui::Document::onUpdate()");
#endif

    std::list<Gui::BaseView*>::iterator it;

    for (it = d->baseViews.begin();it != d->baseViews.end();++it) {
        (*it)->onUpdate();
    }

    for (it = d->passiveViews.begin();it != d->passiveViews.end();++it) {
        (*it)->onUpdate();
    }
}

void Document::onRelabel()
{
#ifdef FC_LOGUPDATECHAIN
    Base::Console().Log("Acti: Gui::Document::onRelabel()");
#endif

    std::list<Gui::BaseView*>::iterator it;

    for (it = d->baseViews.begin();it != d->baseViews.end();++it) {
        (*it)->onRelabel(this);
    }

    for (it = d->passiveViews.begin();it != d->passiveViews.end();++it) {
        (*it)->onRelabel(this);
    }

    d->connectChangeDocumentBlocker.unblock();
}

bool Document::isLastView()
{
    if (d->baseViews.size() <= 1)
        return true;
    return false;
}

/**
 *  This method checks if the document can be closed. It checks on
 *  the save state of the document and is able to abort the closing.
 */
bool Document::canClose (bool checkModify, bool checkLink){return 0;}

std::list<MDIView*> Document::getMDIViews() const
{
    std::list<MDIView*> views;
    for (std::list<BaseView*>::const_iterator it = d->baseViews.begin();
         it != d->baseViews.end(); ++it) {
        MDIView* view = dynamic_cast<MDIView*>(*it);
        if (view)
            views.push_back(view);
    }

    return views;
}

std::list<MDIView*> Document::getMDIViewsOfType(const Base::Type& typeId) const
{
    std::list<MDIView*> views;
    for (std::list<BaseView*>::const_iterator it = d->baseViews.begin();
         it != d->baseViews.end(); ++it) {
        MDIView* view = dynamic_cast<MDIView*>(*it);
        if (view && view->isDerivedFrom(typeId))
            views.push_back(view);
    }

    return views;
}

/// send messages to the active view
bool Document::sendMsgToViews(const char* pMsg)
{
    std::list<Gui::BaseView*>::iterator it;
    const char** pReturnIgnore=nullptr;

    for (it = d->baseViews.begin();it != d->baseViews.end();++it) {
        if ((*it)->onMsg(pMsg,pReturnIgnore)) {
            return true;
        }
    }

    for (it = d->passiveViews.begin();it != d->passiveViews.end();++it) {
        if ((*it)->onMsg(pMsg,pReturnIgnore)) {
            return true;
        }
    }

    return false;
}

bool Document::sendMsgToFirstView(const Base::Type& typeId, const char* pMsg, const char** ppReturn)
{
    // first try the active view
    Gui::MDIView* view = getActiveView();
    if (view && view->isDerivedFrom(typeId)) {
        if (view->onMsg(pMsg, ppReturn))
            return true;
    }

    // now try the other views
    std::list<Gui::MDIView*> views = getMDIViewsOfType(typeId);
    for (std::list<Gui::MDIView*>::iterator it = views.begin(); it != views.end(); ++it) {
        if ((*it != view) && (*it)->onMsg(pMsg, ppReturn)) {
            return true;
        }
    }

    return false;
}

/// Getter for the active view
MDIView* Document::getActiveView() const
{
    // get the main window's active view
    // MDIView* active = getMainWindow()->activeWindow();
    MDIView* active = nullptr;

    // get all MDI views of the document
    std::list<MDIView*> mdis = getMDIViews();

    // check whether the active view is part of this document
    bool ok=false;
    for (std::list<MDIView*>::const_iterator it = mdis.begin(); it != mdis.end(); ++it) {
        if ((*it) == active) {
            ok = true;
            break;
        }
    }

    if (ok)
        return active;

    // the active view is not part of this document, just use the last view
    // const auto &windows =Gui::Application::Instance->main_widget->windows();
    // const auto &windows =Gui::Application::Instance->main_widget->windows();
    const QList<QWidget*> windows;
    for(auto rit=mdis.rbegin();rit!=mdis.rend();++rit) {
        // Some view is removed from window list for some reason, e.g. TechDraw
        // hidden page has view but not in the list. By right, the view will
        // self delete, but not the case for TechDraw, especially during
        // document restore.
        if(windows.contains(*rit) || (*rit)->isDerivedFrom(View3DInventor::getClassTypeId()))
            return *rit;
    }
    return nullptr;
}

MDIView *Document::setActiveView(ViewProviderDocumentObject *vp, Base::Type typeId){return nullptr;}

/**
 * @brief Document::setActiveWindow
 * If this document is active and the view is part of it then it will be
 * activated. If the document is not active of the view is already active
 * nothing is done.
 * @param view
 */
void Document::setActiveWindow(Gui::MDIView* view)
{
    // get the main window's active view
    // MDIView* active = getMainWindow()->activeWindow();
    MDIView* active = nullptr;

    // view is already active
    if (active == view)
        return;

    // get all MDI views of the document
    std::list<MDIView*> mdis = getMDIViews();

    // this document is not active
    if (std::find(mdis.begin(), mdis.end(), active) == mdis.end())
        return;

    // the view is not part of the document
    if (std::find(mdis.begin(), mdis.end(), view) == mdis.end())
        return;

    // getMainWindow()->setActiveWindow(view);
}

Gui::MDIView* Document::getViewOfNode(SoNode* node) const
{
    std::list<MDIView*> mdis = getMDIViewsOfType(View3DInventor::getClassTypeId());
    for (std::list<MDIView*>::const_iterator it = mdis.begin(); it != mdis.end(); ++it) {
        View3DInventor* view = static_cast<View3DInventor*>(*it);
        if (view->getViewer()->searchNode(node))
            return *it;
    }

    return nullptr;
}

Gui::MDIView* Document::getViewOfViewProvider(const Gui::ViewProvider* vp) const{return nullptr;}

Gui::MDIView* Document::getEditingViewOfViewProvider(Gui::ViewProvider* vp) const
{
    std::list<MDIView*> mdis = getMDIViewsOfType(View3DInventor::getClassTypeId());
    for (std::list<MDIView*>::const_iterator it = mdis.begin(); it != mdis.end(); ++it) {
        View3DInventor* view = static_cast<View3DInventor*>(*it);
        View3DInventorViewer* viewer = view->getViewer();
        // there is only one 3d view which is in edit mode
        if (viewer->hasViewProvider(vp) && viewer->isEditingViewProvider())
            return *it;
    }

    return nullptr;
}

//--------------------------------------------------------------------------
// UNDO REDO transaction handling
//--------------------------------------------------------------------------
/** Open a new Undo transaction on the active document
 *  This method opens a new UNDO transaction on the active document. This transaction
 *  will later appear in the UNDO/REDO dialog with the name of the command. If the user
 *  recall the transaction everything changed on the document between OpenCommand() and
 *  CommitCommand will be undone (or redone). You can use an alternative name for the
 *  operation default is the command name.
 *  @see CommitCommand(),AbortCommand()
 */
void Document::openCommand(const char* sName)
{
    getDocument()->openTransaction(sName);
}

void Document::commitCommand()
{
    getDocument()->commitTransaction();
}

void Document::abortCommand()
{
    getDocument()->abortTransaction();
}

bool Document::hasPendingCommand() const
{
    return getDocument()->hasPendingTransaction();
}

/// Get a string vector with the 'Undo' actions
std::vector<std::string> Document::getUndoVector() const
{
    return getDocument()->getAvailableUndoNames();
}

/// Get a string vector with the 'Redo' actions
std::vector<std::string> Document::getRedoVector() const
{
    return getDocument()->getAvailableRedoNames();
}

bool Document::checkTransactionID(bool undo, int iSteps){return 0;}

bool Document::isPerformingTransaction() const {
    return d->_isTransacting;
}

/// Will UNDO one or more steps
void Document::undo(int iSteps){}

/// Will REDO one or more steps
void Document::redo(int iSteps){}


void Document::handleChildren3D(ViewProvider* viewProvider, bool deleting){}

void Document::toggleInSceneGraph(ViewProvider *vp){}

void Document::slotChangePropertyEditor(const App::Document &doc, const App::Property &Prop){}